package cn.xyt.server;

import java.net.InetSocketAddress;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.tom.kit.IoBuffer;
import cn.xyt.dto.Header;
import cn.xyt.dto.Message;
import cn.xyt.util.DeviceCache;
import cn.xyt.util.DeviceCache.InetChannel;
import cn.xyt.util.MessageThreadPool;
import cn.xyt.util.NettyContextUtil;
import cn.xyt.util.UDPUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageCodec;

@Service
@Sharable
public class MessageCoder extends MessageToMessageCodec<DatagramPacket, Message> {

	@Autowired
	private MessageController messageController;
	
	private final Logger logger = LoggerFactory.getLogger(MessageToMessageCodec.class);
	
	@Override
	protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
		ByteBuf buf = msg.content();
		try{
			byte[]  array = new byte[buf.readableBytes()];
			buf.readBytes(array);
			// 转义
			byte[] array0 = UDPUtils.doEscape4Receive(array, 0, array.length);
			Message msg0 = bufToMessage(array0);
			InetSocketAddress sender0 = msg.sender();
			setInetSocketAdd(ctx, msg0, sender0);
			
//		ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("123123123", CharsetUtil.UTF_8), sender0));
			addTask(ctx, msg0);
		}finally{
			buf.release();
		}
	}
	
	public void setInetSocketAdd(ChannelHandlerContext ctx,Message msg, InetSocketAddress sender0){
		String deviceid = "" + msg.getHeader().getDeviceidByByte();
		InetChannel sender = DeviceCache.get(deviceid);
		if ((sender == null) || (!sender.equals(sender0))) { // 保存 通道信息
			DeviceCache.set(deviceid, new InetChannel(sender0, ctx.channel()) );
			NettyContextUtil.setAttr(ctx, "id", deviceid);
		}
	}
	
	 
	@Override
	protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> out) throws Exception {
		long deviceid = msg.getHeader().getDeviceidByByte();
		InetChannel sender = DeviceCache.get(""+deviceid);
		if(sender == null) {
			throw new IllegalArgumentException("sender address is null");
		}
		out.add(new DatagramPacket(messageTobuf(msg), sender.address)) ;
	}
	
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		logger.error("MessageCoder -->", cause);
	}
	
	//8E 88
	//50 44 05 A6 C4
	//00 AB 00 09   // 
	//AA 00 00 A1 01 01 0B  // 透传内容
	//59
	//8F
	private Message bufToMessage(byte[] b) {
		IoBuffer buf = IoBuffer.wrap(b);
		buf.readShort(); // 8e 88
		//8E 88 50 44 05 A6 C4 00 BB 00 07 00 00 00 00 01 46 8F
		Header header = new Header();
		header.setDeviceid(buf.readBytes(5));
		header.setMsgid(buf.readShort());
		short len = buf.readShort();
		header.setLength(len);
		Message msg = new Message(header);
		msg.setBody(buf.readBytes(len-2));
		logger.info(msg.toString());
		return msg;
	}

	
	private ByteBuf messageTobuf(Message msg) {
		msg.setBodyCheck(); // body 的校验
		/* 重新调整 header 的长度 */
		msg.getHeader().setLength(msg.getBody()!=null ? (short)(msg.getBody().length+2) : 0);
		/*生成总校验码*/
		msg.intCheck(); 
		
		IoBuffer buf = IoBuffer.allocate(30);
		buf.writeBytes(new byte[]{(byte)0x8e,(byte) 0x88 });
		buf.writeBytes(msg.getHeader().toByteBuf());
		buf.writeBytes(msg.getBody());
		buf.writeByte((0x88 ^ msg.getCheck()));
		buf.writeByte(0x8f);
		buf.flip();
		//  转义
		ByteBuf array = Unpooled.wrappedBuffer(buf.readBytes(buf.limit()));
		return array;
	}
	
	

	protected void addTask(final ChannelHandlerContext ctx, final Message message) throws Exception {
	
		//  处理心跳, 不到业务处理
		// 直接返回
		if(message.getHeader().getMsgid() == 0xbb){
			ctx.channel().writeAndFlush(message);
			return;
		}
		
		/** 执行业务处理 */
		MessageThreadPool.executeMessageTask(new Runnable() {
			@Override
			public void run() {
				try {
					boolean needResp = messageController.exec(message);
					if (needResp) {
						ctx.channel().writeAndFlush(message);
					}
				} catch (Exception e) {
					e.printStackTrace();
					throw new RuntimeException(e);
				}
			}
		});

	}

}
